#ifndef __SCENE_H__
#define __SCENE_H__

#include <vector>
#include <algorithm>
#include <functional>
#include "common/nvMath.hh"
#include "math/math.hh"

/**
 *
 */
class SceneObject
{
    public:
        SceneObject( void )
        {
            mOrientation.set_translate( nv::vec3f( .0f, .0f, -200.0f ) );
        }
                
        virtual ~SceneObject( void )
        {
        }
        
        virtual void render( void ) = 0;
    public:
        nv::matrix4f    mOrientation;
};

/**
 *
 */
class Cube : public SceneObject
{
    public:
        Cube( const nv::vec3f& aExtents ) : mExtents( aExtents )
        {
            recalculate();
        }
    public:
        void render( void )
        {
            glMatrixMode(GL_MODELVIEW);
            glLoadMatrixf( mOrientation.get_value() );
            
            glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
            
            // Draw a cube
            glBegin(GL_QUADS);
                glTexCoord2f(0, 0); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMin[ 2 ] );
                glTexCoord2f(0, 1); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMin[ 2 ] );

                glTexCoord2f(0, 0); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMax[ 2 ] );
                glTexCoord2f(0, 1); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMax[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMax[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMax[ 2 ] );

                glTexCoord2f(0, 0); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMin[ 2 ] );
                glTexCoord2f(0, 1); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMax[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMax[ 2 ] );

                glTexCoord2f(0, 0); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMin[ 2 ] );
                glTexCoord2f(0, 1); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMax[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMax[ 2 ] );

                glTexCoord2f(0, 1); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMax[ 2 ] );
                glTexCoord2f(0, 0); glVertex3f( mMin[ 0 ], mMin[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMax[ 0 ], mMin[ 1 ], mMax[ 2 ] );

                glTexCoord2f(0, 1); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMax[ 2 ] );
                glTexCoord2f(0, 0); glVertex3f( mMin[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 0); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMin[ 2 ] );
                glTexCoord2f(1, 1); glVertex3f( mMax[ 0 ], mMax[ 1 ], mMax[ 2 ] );
            glEnd();
        }
        
    private:
        void recalculate( void )
        {
            mMax = nv::vec3f( mExtents[ 0 ], mExtents[ 1 ], mExtents[ 2 ] );
            mMin = -mMax;
        }
        
    public:
        nv::vec3f mExtents;
        nv::vec3f mMax;
        nv::vec3f mMin;        
};


struct deleter_ {
template< typename T > void operator()( T* t ) { delete t; }
} deleter;

/**
 *
 */
class Scene_
{
public:
    typedef std::vector< SceneObject* > SceneObjects;
    typedef SceneObjects::size_type SceneObjectId;
    
public:
    Scene_( void )
    {
    }
    
    ~Scene_( void )
    {
        std::for_each( mSceneObjects.begin(), mSceneObjects.end(), deleter );
        mSceneObjects.clear();
    }
    
public:
    SceneObject* addSceneObject( SceneObject* aSceneObject )
    {
        mSceneObjects.push_back( aSceneObject );
        return aSceneObject;
    }
            
    void renderScene( void )
    {
        
		// Setup modelview matrix
      std::for_each( mSceneObjects.begin(), mSceneObjects.end(), std::mem_fun( &SceneObject::render ) );
    }
private:
    SceneObjects    mSceneObjects;
}Scene;

#endif